home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 21 / AACD 21.iso / AACD / Utilities / Ghostscript / src / genconf.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-01  |  20.6 KB  |  821 lines

  1. /* Copyright (C) 1993, 1996, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of AFPL Ghostscript.
  4.   
  5.   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
  6.   distributor accepts any responsibility for the consequences of using it, or
  7.   for whether it serves any particular purpose or works at all, unless he or
  8.   she says so in writing.  Refer to the Aladdin Free Public License (the
  9.   "License") for full details.
  10.   
  11.   Every copy of AFPL Ghostscript must include a copy of the License, normally
  12.   in a plain ASCII text file named PUBLIC.  The License grants you the right
  13.   to copy, modify and redistribute AFPL Ghostscript, but only under certain
  14.   conditions described in the License.  Among other things, the License
  15.   requires that the copyright notice and this notice be preserved on all
  16.   copies.
  17. */
  18.  
  19. /*$Id: genconf.c,v 1.3 2000/09/19 19:00:23 lpd Exp $ */
  20. /* Generate configuration files */
  21. #include "stdpre.h"
  22. #include <assert.h>
  23. #include <ctype.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>        /* for calloc */
  26. #include <string.h>
  27.  
  28. /*
  29.  * We would like to use the real realloc, but it doesn't work on all systems
  30.  * (e.g., some Linux versions).  Also, this procedure does the right thing
  31.  * if old_ptr = NULL.
  32.  */
  33. private void *
  34. mrealloc(void *old_ptr, size_t old_size, size_t new_size)
  35. {
  36.     void *new_ptr = malloc(new_size);
  37.  
  38.     if (new_ptr == NULL)
  39.     return NULL;
  40.     /* We have to pass in the old size, since we have no way to */
  41.     /* determine it otherwise. */
  42.     if (old_ptr)
  43.     memcpy(new_ptr, old_ptr, min(old_size, new_size));
  44.     return new_ptr;
  45. }
  46.  
  47. /*
  48.  * This program generates a set of configuration files.
  49.  * Usage:
  50.  *      genconf [-Z] [-e escapechar] [-n [name_prefix | -]] [@]xxx.dev*
  51.  *        [-f gconfigf.h] [-h gconfig.h]
  52.  *        [-p[l|L][u][e] pattern] [-l|o|lo|ol out.tr]
  53.  * The default escape character is &.  When this character appears in a
  54.  * pattern, it acts as follows:
  55.  *    &p produces a %;
  56.  *    &s produces a space;
  57.  *    && (i.e., the escape character twice) produces a \;
  58.  *    &- produces a -;
  59.  *    &x, for any other character x, is an error.
  60.  */
  61.  
  62. #define DEFAULT_NAME_PREFIX "gs_"
  63.  
  64. #define MAX_STR 120
  65.  
  66. /* Structures for accumulating information. */
  67. typedef struct string_item_s {
  68.     const char *str;
  69.     int file_index;        /* index of file containing this item */
  70.     int index;
  71. } string_item;
  72.  
  73. /* The values of uniq_mode are bit masks. */
  74. typedef enum {
  75.     uniq_all = 1,        /* keep all occurrences (default) */
  76.     uniq_first = 2,        /* keep only first occurrence */
  77.     uniq_last = 4        /* keep only last occurrence */
  78. } uniq_mode;
  79. typedef struct string_list_s {
  80.     /* The following are set at creation time. */
  81.     const char *list_name;    /* only for debugging */
  82.     int max_count;
  83.     uniq_mode mode;
  84.     /* The following are updated dynamically. */
  85.     int count;
  86.     string_item *items;
  87. } string_list;
  88.  
  89. #define max_pattern 60
  90. typedef struct string_pattern_s {
  91.     bool upper_case;
  92.     bool drop_extn;
  93.     char pattern[max_pattern + 1];
  94. } string_pattern;
  95. typedef struct config_s {
  96.     int debug;
  97.     const char *name_prefix;
  98.     const char *file_prefix;
  99.     /* Special "resources" */
  100.     string_list file_names;
  101.     string_list file_contents;
  102.     string_list replaces;
  103.     /* Real resources */
  104.     union ru_ {
  105.     struct nu_ {
  106.         string_list sorted_resources;
  107. #define c_sorted_resources lists.named.sorted_resources
  108.         string_list resources;
  109. #define c_resources lists.named.resources
  110.         string_list devs;    /* also includes devs2 */
  111. #define c_devs lists.named.devs
  112.         string_list fonts;
  113. #define c_fonts lists.named.fonts
  114.         string_list libs;
  115. #define c_libs lists.named.libs
  116.         string_list libpaths;
  117. #define c_links lists.named.links
  118.         string_list links;
  119. #define c_libpaths lists.named.libpaths
  120.         string_list objs;
  121. #define c_objs lists.named.objs
  122.     } named;
  123. #define NUM_RESOURCE_LISTS 8
  124.     string_list indexed[NUM_RESOURCE_LISTS];
  125.     } lists;
  126.     string_pattern lib_p;
  127.     string_pattern libpath_p;
  128.     string_pattern obj_p;
  129. } config;
  130.  
  131. /* These lists grow automatically if needed, so we could start out with */
  132. /* small allocations. */
  133. static const config init_config =
  134. {
  135.     0,                /* debug */
  136.     DEFAULT_NAME_PREFIX,    /* name_prefix */
  137.     "",                /* file_prefix */
  138.     {"file name", 200},        /* file_names */
  139.     {"file contents", 200},    /* file_contents */
  140.     {"-replace", 50}
  141. };
  142. static const string_list init_config_lists[] =
  143. {
  144.     {"resource", 100, uniq_first},
  145.     {"sorted_resource", 20, uniq_first},
  146.     {"-dev", 100, uniq_first},
  147.     {"-font", 50, uniq_first},
  148.     {"-lib", 20, uniq_last},
  149.     {"-libpath", 10, uniq_first},
  150.     {"-link", 10, uniq_first},
  151.     {"-obj", 500, uniq_first}
  152. };
  153.  
  154. /* Forward definitions */
  155. int alloc_list(P1(string_list *));
  156. void dev_file_name(P1(char *));
  157. int process_replaces(P1(config *));
  158. int read_dev(P2(config *, const char *));
  159. int read_token(P3(char *, int, const char **));
  160. int add_entry(P4(config *, char *, const char *, int));
  161. string_item *add_item(P3(string_list *, const char *, int));
  162. void sort_uniq(P2(string_list *, bool));
  163. void write_list(P3(FILE *, const string_list *, const char *));
  164. void write_list_pattern(P3(FILE *, const string_list *, const string_pattern *));
  165. bool var_expand(P3(char *, char [MAX_STR], const config *));
  166. void add_definition(P4(const char *, const char *, string_list *, bool));
  167. string_item *lookup(P2(const char *, const string_list *));
  168.  
  169. int
  170. main(int argc, char *argv[])
  171. {
  172.     config conf;
  173.     char escape = '&';
  174.     int i;
  175.  
  176.     /* Allocate string lists. */
  177.     conf = init_config;
  178.     memcpy(conf.lists.indexed, init_config_lists,
  179.        sizeof(conf.lists.indexed));
  180.     alloc_list(&conf.file_names);
  181.     alloc_list(&conf.file_contents);
  182.     alloc_list(&conf.replaces);
  183.     for (i = 0; i < NUM_RESOURCE_LISTS; ++i)
  184.     alloc_list(&conf.lists.indexed[i]);
  185.  
  186.     /* Initialize patterns. */
  187.     conf.lib_p.upper_case = false;
  188.     conf.lib_p.drop_extn = false;
  189.     strcpy(conf.lib_p.pattern, "%s\n");
  190.     conf.libpath_p = conf.lib_p;
  191.     conf.obj_p = conf.lib_p;
  192.  
  193.     /* Process command line arguments. */
  194.     for (i = 1; i < argc; i++) {
  195.     const char *arg = argv[i];
  196.     FILE *out;
  197.     int lib = 0, obj = 0;
  198.  
  199.     if (*arg != '-') {
  200.         read_dev(&conf, arg);
  201.         continue;
  202.     }
  203.     if (i == argc - 1) {
  204.         fprintf(stderr, "Missing argument after %s.\n",
  205.             arg);
  206.         exit(1);
  207.     }
  208.     switch (arg[1]) {
  209.         case 'C':        /* change directory, by analogy with make */
  210.         conf.file_prefix =
  211.             (argv[i + 1][0] == '-' ? "" : argv[i + 1]);
  212.         ++i;
  213.         continue;
  214.         case 'e':
  215.         escape = argv[i + 1][0];
  216.         ++i;
  217.         continue;
  218.         case 'n':
  219.         conf.name_prefix =
  220.             (argv[i + 1][0] == '-' ? "" : argv[i + 1]);
  221.         ++i;
  222.         continue;
  223.         case 'p':
  224.         {
  225.             string_pattern *pat;
  226.  
  227.             switch (*(arg += 2)) {
  228.             case 'l':
  229.                 pat = &conf.lib_p;
  230.                 break;
  231.             case 'L':
  232.                 pat = &conf.libpath_p;
  233.                 break;
  234.             default:
  235.                 pat = &conf.obj_p;
  236.                 arg--;
  237.             }
  238.             pat->upper_case = false;
  239.             pat->drop_extn = false;
  240.             if (argv[i + 1][0] == '-')
  241.             strcpy(pat->pattern, "%s\n");
  242.             else {
  243.             char *p, *q;
  244.  
  245.             for (p = pat->pattern, q = argv[++i];
  246.                  (*p++ = *q++) != 0;
  247.                 )
  248.                 if (p[-1] == escape)
  249.                 switch (*q) {
  250.                     case 'p':
  251.                     p[-1] = '%'; q++; break;
  252.                     case 's':
  253.                     p[-1] = ' '; q++; break;
  254.                     case '-':
  255.                     p[-1] = '-'; q++; break;
  256.                     default:
  257.                     if (*q == escape) {
  258.                         p[-1] = '\\'; q++; break;
  259.                     }
  260.                     fprintf(stderr,
  261.                       "%c not followed by p|s|%c|-: &%c\n",
  262.                         escape, escape, *q);
  263.                     exit(1);
  264.                 }
  265.             p[-1] = '\n';
  266.             *p = 0;
  267.             }
  268.             for (;;) {
  269.             switch (*++arg) {
  270.                 case 'u':
  271.                 pat->upper_case = true;
  272.                 break;
  273.                 case 'e':
  274.                 pat->drop_extn = true;
  275.                 break;
  276.                 case 0:
  277.                 goto pbreak;
  278.                 default:
  279.                 fprintf(stderr, "Unknown switch %s.\n", arg);
  280.                 exit(1);
  281.             }
  282.             }
  283.           pbreak:if (pat == &conf.obj_p) {
  284.             conf.lib_p = *pat;
  285.             conf.libpath_p = *pat;
  286.             }
  287.             continue;
  288.         }
  289.         case 'Z':
  290.         conf.debug = 1;
  291.         continue;
  292.     }
  293.     /* Must be an output file. */
  294.     out = fopen(argv[++i], "w");
  295.     if (out == 0) {
  296.         fprintf(stderr, "Can't open %s for output.\n",
  297.             argv[i]);
  298.         exit(1);
  299.     }
  300.     switch (arg[1]) {
  301.         case 'f':
  302.         process_replaces(&conf);
  303.         fputs("/* This file was generated automatically by genconf.c. */\n", out);
  304.         fputs("/* For documentation, see gsconfig.c. */\n", out);
  305.         {
  306.             char template[80];
  307.  
  308.             sprintf(template,
  309.                 "font_(\"0.font_%%s\",%sf_%%s,zf_%%s)\n",
  310.                 conf.name_prefix);
  311.             write_list(out, &conf.c_fonts, template);
  312.         }
  313.         break;
  314.         case 'h':
  315.         process_replaces(&conf);
  316.         fputs("/* This file was generated automatically by genconf.c. */\n", out);
  317.         write_list(out, &conf.c_devs, "%s\n");
  318.         sort_uniq(&conf.c_resources, true);
  319.         write_list(out, &conf.c_resources, "%s\n");
  320.         sort_uniq(&conf.c_sorted_resources, false);
  321.         write_list(out, &conf.c_sorted_resources, "%s\n");
  322.         break;
  323.         case 'l':
  324.         lib = 1;
  325.         obj = arg[2] == 'o';
  326.         goto lo;
  327.         case 'o':
  328.         obj = 1;
  329.         lib = arg[2] == 'l';
  330.           lo:process_replaces(&conf);
  331.         if (obj) {
  332.             sort_uniq(&conf.c_objs, true);
  333.             write_list_pattern(out, &conf.c_objs, &conf.obj_p);
  334.         }
  335.         if (lib) {
  336.             sort_uniq(&conf.c_libs, true);
  337.             sort_uniq(&conf.c_links, true);
  338.             write_list_pattern(out, &conf.c_libpaths, &conf.libpath_p);
  339.             write_list_pattern(out, &conf.c_links, &conf.obj_p);
  340.             write_list_pattern(out, &conf.c_libs, &conf.lib_p);
  341.         }
  342.         break;
  343.         default:
  344.         fclose(out);
  345.         fprintf(stderr, "Unknown switch %s.\n", argv[i]);
  346.         exit(1);
  347.     }
  348.     fclose(out);
  349.     }
  350.  
  351.     return 0;
  352. }
  353.  
  354. /* Allocate and initialize a string list. */
  355. int
  356. alloc_list(string_list * list)
  357. {
  358.     list->count = 0;
  359.     list->items =
  360.     (string_item *) calloc(list->max_count, sizeof(string_item));
  361.     assert(list->items != NULL);
  362.     return 0;
  363. }
  364.  
  365. /* If necessary, convert a .dev name to its file name. */
  366. void
  367. dev_file_name(char *str)
  368. {
  369.     int len = strlen(str);
  370.  
  371.     if (len <= 4 || strcmp(".dev", str + len - 4))
  372.     strcat(str, ".dev");
  373. }
  374.  
  375. /* Delete any files that are named as -replace "resources". */
  376. int
  377. process_replaces(config * pconf)
  378. {
  379.     char bufname[MAX_STR];
  380.     int i;
  381.  
  382.     for (i = 0; i < pconf->replaces.count; ++i) {
  383.     int j;
  384.  
  385.     strcpy(bufname, pconf->replaces.items[i].str);
  386.     /* See if the file being replaced was included. */
  387.     dev_file_name(bufname);
  388.     for (j = 0; j < pconf->file_names.count; ++j) {
  389.         const char *fname = pconf->file_names.items[j].str;
  390.  
  391.         if (!strcmp(fname, bufname)) {
  392.         if (pconf->debug)
  393.             printf("Replacing file %s.\n", fname);
  394.         /* Delete all resources associated with this file. */
  395.         {
  396.             int rn;
  397.  
  398.             for (rn = 0; rn < NUM_RESOURCE_LISTS; ++rn) {
  399.             string_item *items = pconf->lists.indexed[rn].items;
  400.             int count = pconf->lists.indexed[rn].count;
  401.             int tn;
  402.  
  403.             for (tn = 0; tn < count; ++tn) {
  404.                 if (items[tn].file_index == j) {
  405.                 /*
  406.                  * Delete the item.  Since we haven't sorted
  407.                  * the items yet, just replace this item
  408.                  * with the last one, but make sure we don't
  409.                  * miss scanning it.
  410.                  */
  411.                 if (pconf->debug)
  412.                     printf("Replacing %s %s.\n",
  413.                      pconf->lists.indexed[rn].list_name,
  414.                        items[tn].str);
  415.                 items[tn--] = items[--count];
  416.                 }
  417.             }
  418.             pconf->lists.indexed[rn].count = count;
  419.             }
  420.         }
  421.         pconf->file_names.items[j].str = "";
  422.         break;
  423.         }
  424.     }
  425.     }
  426.     /* Don't process the replaces again. */
  427.     pconf->replaces.count = 0;
  428.     return 0;
  429. }
  430.  
  431. /* Read an entire file into memory. */
  432. /* We use the 'index' of the file_contents string_item to record the union */
  433. /* of the uniq_modes of all (direct and indirect) items in the file. */
  434. /* Return the file_contents item for the file. */
  435. private string_item *
  436. read_file(config * pconf, const char *fname)
  437. {
  438.     char *cname = malloc(strlen(fname) + strlen(pconf->file_prefix) + 1);
  439.     int i;
  440.     FILE *in;
  441.     int end, nread;
  442.     char *cont;
  443.     string_item *item;
  444.  
  445.     if (cname == 0) {
  446.     fprintf(stderr, "Can't allocate space for file name %s%s.\n",
  447.         pconf->file_prefix, fname);
  448.     exit(1);
  449.     }
  450.     strcpy(cname, pconf->file_prefix);
  451.     strcat(cname, fname);
  452.     for (i = 0; i < pconf->file_names.count; ++i)
  453.     if (!strcmp(pconf->file_names.items[i].str, cname)) {
  454.         free(cname);
  455.         return &pconf->file_contents.items[i];
  456.     }
  457.     /* Try to open the file in binary mode, to avoid the overhead */
  458.     /* of unnecessary EOL conversion in the C library. */
  459.     in = fopen(cname, "rb");
  460.     if (in == 0) {
  461.     in = fopen(cname, "r");
  462.     if (in == 0) {
  463.         fprintf(stderr, "Can't read %s.\n", cname);
  464.         exit(1);
  465.     }
  466.     }
  467.     fseek(in, 0L, 2 /*SEEK_END */ );
  468.     end = ftell(in);
  469.     cont = malloc(end + 1);
  470.     if (cont == 0) {
  471.     fprintf(stderr, "Can't allocate %d bytes to read %s.\n",
  472.         end + 1, cname);
  473.     exit(1);
  474.     }
  475.     rewind(in);
  476.     nread = fread(cont, 1, end, in);
  477.     fclose(in);
  478.     cont[nread] = 0;
  479.     if (pconf->debug)
  480.     printf("File %s = %d bytes.\n", cname, nread);
  481.     add_item(&pconf->file_names, cname, -1);
  482.     item = add_item(&pconf->file_contents, cont, -1);
  483.     item->index = 0;        /* union of uniq_modes */
  484.     return item;
  485. }
  486.  
  487. /* Read and parse a .dev file. */
  488. /* Return the union of all its uniq_modes. */
  489. int
  490. read_dev(config * pconf, const char *arg)
  491. {
  492.     string_item *item;
  493.     const char *in;
  494.  
  495. #define MAX_TOKEN 256
  496.     char *token = malloc(MAX_TOKEN + 1);
  497.     char *category = malloc(MAX_TOKEN + 1);
  498.     int file_index;
  499.     int len;
  500.  
  501.     if (pconf->debug)
  502.     printf("Reading %s;\n", arg);
  503.     item = read_file(pconf, arg);
  504.     if (item->index == uniq_first) {    /* Don't need to read the file again. */
  505.     if (pconf->debug)
  506.         printf("Skipping duplicate file.\n");
  507.     return uniq_first;
  508.     }
  509.     in = item->str;
  510.     file_index = item - pconf->file_contents.items;
  511.     strcpy(category, "obj");
  512.     while ((len = read_token(token, MAX_TOKEN, &in)) > 0)
  513.     item->index |= add_entry(pconf, category, token, file_index);
  514.     free(category);
  515. #undef MAX_TOKEN
  516.     if (len < 0) {
  517.     fprintf(stderr, "Token too long: %s.\n", token);
  518.     exit(1);
  519.     }
  520.     if (pconf->debug)
  521.     printf("Finished %s.\n", arg);
  522.     free(token);
  523.     return item->index;
  524. }
  525.  
  526. /* Read a token from a string that contains the contents of a file. */
  527. int
  528. read_token(char *token, int max_len, const char **pin)
  529. {
  530.     const char *in = *pin;
  531.     int len = 0;
  532.  
  533.     while (len < max_len) {
  534.     char ch = *in;
  535.  
  536.     if (ch == 0)
  537.         break;
  538.     ++in;
  539.     if (isspace(ch)) {
  540.         if (len > 0)
  541.         break;
  542.         continue;
  543.     }
  544.     token[len++] = ch;
  545.     }
  546.     token[len] = 0;
  547.     *pin = in;
  548.     return (len >= max_len ? -1 /* token too long */ : len);
  549. }
  550.  
  551. /* Add an entry to a configuration. */
  552. /* Return its uniq_mode. */
  553. int
  554. add_entry(config * pconf, char *category, const char *item, int file_index)
  555. {
  556.     if (item[0] == '-' && islower(item[1])) {    /* set category */
  557.     strcpy(category, item + 1);
  558.     return 0;
  559.     } else {            /* add to current category */
  560.     char str[MAX_STR];
  561.     char template[80];
  562.     const char *pat = 0;
  563.     string_list *list = &pconf->c_resources;
  564.  
  565.     if (pconf->debug)
  566.         printf("Adding %s %s;\n", category, item);
  567.     /* Handle a few resources specially; just queue the rest. */
  568.     switch (category[0]) {
  569. #define IS_CAT(str) !strcmp(category, str)
  570.         case 'd':
  571.         if (IS_CAT("dev"))
  572.             pat = "device_(%s%%s_device)";
  573.         else if (IS_CAT("dev2"))
  574.             pat = "device2_(%s%%s_device)";
  575.         else
  576.             goto err;
  577.         list = &pconf->c_devs;
  578. pre:        sprintf(template, pat, pconf->name_prefix);
  579.         pat = template;
  580.         break;
  581.         case 'e':
  582.         if (IS_CAT("emulator")) {
  583.             sprintf(str, "emulator_(\"%s\",%u)",
  584.                 item, (uint)strlen(item));
  585.             item = str;
  586.             break;
  587.         }
  588.         goto err;
  589.         case 'f':
  590.         if (IS_CAT("font")) {
  591.             list = &pconf->c_fonts;
  592.             break;
  593.         } else if (IS_CAT("functiontype")) {
  594.             pat = "function_type_(%%s,%sbuild_function_%%s)";
  595.         } else
  596.             goto err;
  597.         goto pre;
  598.         case 'h':
  599.         if (IS_CAT("halftone")) {
  600.             pat = "halftone_(%sdht_%%s)";
  601.         } else
  602.             goto err;
  603.         goto pre;
  604.         case 'i':
  605.         if (IS_CAT("imageclass")) {
  606.             list = &pconf->c_sorted_resources;
  607.             pat = "image_class_(%simage_class_%%s)";
  608.         } else if (IS_CAT("imagetype")) {
  609.             pat = "image_type_(%%s,%simage_type_%%s)";
  610.         } else if (IS_CAT("include")) {
  611.             strcpy(str, item);
  612.             dev_file_name(str);
  613.             return read_dev(pconf, str);
  614.         } else if (IS_CAT("init")) {
  615.             pat = "init_(%s%%s_init)";
  616.         } else if (IS_CAT("iodev")) {
  617.             pat = "io_device_(%siodev_%%s)";
  618.         } else
  619.             goto err;
  620.         goto pre;
  621.         case 'l':
  622.         if (IS_CAT("lib")) {
  623.             list = &pconf->c_libs;
  624.             break;
  625.         } else if (IS_CAT("libpath")) {
  626.             list = &pconf->c_libpaths;
  627.             break;
  628.         } else if (IS_CAT("link")) {
  629.             list = &pconf->c_links;
  630.             break;
  631.         }
  632.         goto err;
  633.         case 'o':
  634.         if (IS_CAT("obj")) {
  635.             list = &pconf->c_objs;
  636.             strcpy(template, pconf->file_prefix);
  637.             strcat(template, "%s");
  638.             pat = template;
  639.             break;
  640.         }
  641.         if (IS_CAT("oper")) {
  642.             pat = "oper_(%s_op_defs)";
  643.             break;
  644.         }
  645.         goto err;
  646.         case 'p':
  647.         if (IS_CAT("ps")) {
  648.             sprintf(str, "psfile_(\"%s.ps\",%u)",
  649.                 item, (uint)(strlen(item) + 3));
  650.             item = str;
  651.             break;
  652.         }
  653.         goto err;
  654.         case 'r':
  655.         if (IS_CAT("replace")) {
  656.             list = &pconf->replaces;
  657.             break;
  658.         }
  659.         goto err;
  660. #undef IS_CAT
  661.         default:
  662. err:        fprintf(stderr, "Definition not recognized: %s %s.\n",
  663.             category, item);
  664.         exit(1);
  665.     }
  666.     if (pat) {
  667.         sprintf(str, pat, item, item);
  668.         assert(strlen(str) < MAX_STR);
  669.         add_item(list, str, file_index);
  670.     } else
  671.         add_item(list, item, file_index);
  672.     return list->mode;
  673.     }
  674. }
  675.  
  676. /* Add an item to a list. */
  677. string_item *
  678. add_item(string_list * list, const char *str, int file_index)
  679. {
  680.     char *rstr = malloc(strlen(str) + 1);
  681.     int count = list->count;
  682.     string_item *item;
  683.  
  684.     if (count >= list->max_count) {
  685.     list->max_count <<= 1;
  686.     if (list->max_count < 20)
  687.         list->max_count = 20;
  688.     list->items =
  689.         (string_item *) mrealloc(list->items,
  690.                      (list->max_count >> 1) *
  691.                      sizeof(string_item),
  692.                      list->max_count *
  693.                      sizeof(string_item));
  694.     assert(list->items != NULL);
  695.     }
  696.     item = &list->items[count];
  697.     item->str = rstr;
  698.     item->index = count;
  699.     item->file_index = file_index;
  700.     strcpy(rstr, str);
  701.     list->count++;
  702.     return item;
  703. }
  704.  
  705. /* Remove duplicates from a list of string_items. */
  706. /* In case of duplicates, remove all but the earliest (if last = false) */
  707. /* or the latest (if last = true). */
  708. #define psi1 ((const string_item *)p1)
  709. #define psi2 ((const string_item *)p2)
  710. private int
  711. cmp_index(const void *p1, const void *p2)
  712. {
  713.     int cmp = psi1->index - psi2->index;
  714.  
  715.     return (cmp < 0 ? -1 : cmp > 0 ? 1 : 0);
  716. }
  717. private int
  718. cmp_str(const void *p1, const void *p2)
  719. {
  720.     return strcmp(psi1->str, psi2->str);
  721. }
  722. #undef psi1
  723. #undef psi2
  724. void
  725. sort_uniq(string_list * list, bool by_index)
  726. {
  727.     string_item *strlist = list->items;
  728.     int count = list->count;
  729.     const string_item *from;
  730.     string_item *to;
  731.     int i;
  732.     bool last = list->mode == uniq_last;
  733.  
  734.     if (count == 0)
  735.     return;
  736.     qsort((char *)strlist, count, sizeof(string_item), cmp_str);
  737.     for (from = to = strlist + 1, i = 1; i < count; from++, i++)
  738.     if (strcmp(from->str, to[-1].str))
  739.         *to++ = *from;
  740.     else if ((last ? from->index > to[-1].index :
  741.           from->index < to[-1].index)
  742.         )
  743.         to[-1] = *from;
  744.     count = to - strlist;
  745.     list->count = count;
  746.     if (by_index)
  747.     qsort((char *)strlist, count, sizeof(string_item), cmp_index);
  748. }
  749.  
  750. /* Write a list of strings using a template. */
  751. void
  752. write_list(FILE * out, const string_list * list, const char *pstr)
  753. {
  754.     string_pattern pat;
  755.  
  756.     pat.upper_case = false;
  757.     pat.drop_extn = false;
  758.     strcpy(pat.pattern, pstr);
  759.     write_list_pattern(out, list, &pat);
  760. }
  761. void
  762. write_list_pattern(FILE * out, const string_list * list, const string_pattern * pat)
  763. {
  764.     int i;
  765.     char macname[40];
  766.     int plen = strlen(pat->pattern);
  767.  
  768.     *macname = 0;
  769.     for (i = 0; i < list->count; i++) {
  770.     const char *lstr = list->items[i].str;
  771.     int len = strlen(lstr);
  772.     char *str = malloc(len + 1);
  773.     int xlen = plen + len * 3;
  774.     char *xstr = malloc(xlen + 1);
  775.     char *alist;
  776.  
  777.     strcpy(str, lstr);
  778.     if (pat->drop_extn) {
  779.         char *dot = str + len;
  780.  
  781.         while (dot > str && *dot != '.')
  782.         dot--;
  783.         if (dot > str)
  784.         *dot = 0, len = dot - str;
  785.     }
  786.     if (pat->upper_case) {
  787.         char *ptr = str;
  788.  
  789.         for (; *ptr; ptr++)
  790.         if (islower(*ptr))
  791.             *ptr = toupper(*ptr);
  792.     }
  793.     /* We repeat str for the benefit of patterns that */
  794.     /* need the argument substituted in more than one place. */
  795.     sprintf(xstr, pat->pattern, str, str, str);
  796.     /* Check to make sure the item is within the scope of */
  797.     /* an appropriate #ifdef, if necessary. */
  798.     alist = strchr(xstr, '(');
  799.     if (alist != 0 && alist != xstr && alist[-1] == '_') {
  800.         *alist = 0;
  801.         if (strcmp(xstr, macname)) {
  802.         if (*macname)
  803.             fputs("#endif\n", out);
  804.         fprintf(out, "#ifdef %s\n", xstr);
  805.         strcpy(macname, xstr);
  806.         }
  807.         *alist = '(';
  808.     } else {
  809.         if (*macname) {
  810.         fputs("#endif\n", out);
  811.         *macname = 0;
  812.         }
  813.     }
  814.     fputs(xstr, out);
  815.     free(xstr);
  816.     free(str);
  817.     }
  818.     if (*macname)
  819.     fputs("#endif\n", out);
  820. }
  821.